/**************************************************************************************
*
*	Copyright (C) 2009 Brent Andrew Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Animation.h
*
*	Description -	Animation class to easily handle animations using bitmaps.
*
*	Comments	-	If you don't understand some part of the code, use MSDN!!!
*						Move the cursor to the function/type then hit F1 in VS.
*
*	Modification History:
*			Name			   Date					Description
*			CodeSushi	-	11/19/2009	-	Creation of this file
**************************************************************************************/

#ifndef _ANIMATION_H_
#define _ANIMATION_H_

#include <windows.h>
#include "../../Pulse Lib/Include/Pulse.h"
#include "Bitmap.h"
#include "BitmapMasked.h"
#include "BitmapAlpha.h"
#include "BitmapRot.h"

class Bitmap;
using namespace Pulse;

// We wrap Bitmap into this struct to make it easy for us
// to add additional informations like collision rects, offset, etc...
struct AnimFrame
{
	AnimFrame( Bitmap *pFrame ) { PSX_ThrowAssert( pFrame, "Pointer is NULL." ); m_pFrame = pFrame ; }
	~AnimFrame( void ) { delete m_pFrame; }

	// TODO: Create a render function. m_pFrame should not be directly used by Animation.

public:
	// We could add a status telling if this is active or not so we can do a 
	// lazy delete.
	Bitmap * m_pFrame;

};

class Animation
{
public:
	enum AnimStatus { STOPPED, PLAYING, PAUSED }; // TODO: Add PLAYING_REVERSED
	enum PlayType { PLAY_ONCE, PLAY_LOOPING };

	// TODO: Implement a constructor that reads a script file and auto loads a series of bitmaps and other info
	Animation( void );
	~Animation( void );

	void AddFrame( AnimFrame *pFrame );
	void AddFrame( Bitmap *pBitmap );

	void ClearFrames();

	void Update( FLOAT dt );
	void Render( HDC bufferDC, HDC spriteDC, INT x, INT y );

	void Play();
	void Stop();
	void Pause();
	void Reset();

	Pulse::BOOL IsPlaying();
	Pulse::BOOL IsDonePlaying();

	FLOAT GetSPF( void ) const;
	PlayType GetPlayType( void )const;
	UINT GetCurrFrame( void ) const;

	void SetPlayType( PlayType type );
	void SetSPF( FLOAT spf );			// Seconds per frame
	void SetCurrFrame( UINT frame );	// Manually override what frame we want to render

	// Collision Check
	Pulse::BOOL TestPoint( INT x, INT y );

private:

	// Missing an implementation in Pulse::Array. Remove().
	Pulse::BOOL RemoveFrame( const AnimFrame *pFrame );

	Array<AnimFrame *>	m_frames;		// Contains all the frames
	UINT				m_currFrame;
	AnimStatus			m_status;		// Tells this animation what to do
	PlayType			m_playType;
	FLOAT				m_spf;			// Seconds per frame. How long does one frame lasts before moving on to the next?
	FLOAT				m_elapsedTime;
	INT					m_height; //Added 1/26/13
	INT					m_width;

};

PSX_INLINE Animation::Animation( void )
: m_currFrame( 0 ), m_status( PLAYING ), m_playType( PLAY_ONCE ), m_spf( 1.0f/13.0f ), m_elapsedTime( 0.0f ) // 1.0f/13.0f is just an arbitrary value
{
	m_height = 0;
	m_width = 0;
}

PSX_INLINE Animation::~Animation( void )
{
	for ( Pulse::SIZE_T i = 0; i < m_frames.GetSize(); ++i )
		delete m_frames[i];
}

PSX_INLINE void Animation::AddFrame( AnimFrame *pFrame )
{
	PSX_ThrowAssert( pFrame, "Pointer is Null." );
	m_frames.PushBack( pFrame );
}

PSX_INLINE Pulse::BOOL Animation::RemoveFrame( const AnimFrame *pFrame )
{
	// NOTE: Array::Remove is NOT IMPLEMENTED!!!!
	PSX_ThrowAssert( pFrame, "Pointer is NULL." );
	PSX_ThrowAssert( NULL, "This function is not implemented." );
	//m_frames.

	return FALSE;
}

PSX_INLINE void Animation::ClearFrames()
{
	m_frames.Clear();
}

PSX_INLINE void Animation::Render( HDC bufferDC, HDC spriteDC, INT x, INT y )
{
	// This is hacked. We need to create a render function for AnimFrame. But this works for now.
	m_frames[m_currFrame]->m_pFrame->Render( bufferDC, spriteDC, x, y );
}

PSX_INLINE void Animation::Play()
{
	m_status = PLAYING;
}

PSX_INLINE void Animation::Stop()
{
	m_status = STOPPED;
	m_currFrame = 0; 
	m_elapsedTime = 0.0f;
}

PSX_INLINE void Animation::Pause()
{
	m_status = PAUSED;
}

PSX_INLINE void Animation::Reset()
{
	m_currFrame = 0;
}

PSX_INLINE Pulse::BOOL Animation::IsPlaying()
{
	return m_status == PLAYING;
}

PSX_INLINE Pulse::BOOL Animation::IsDonePlaying()
{
	return m_status != PLAYING;
}

PSX_INLINE FLOAT Animation::GetSPF( void ) const
{
	return m_spf;
}

PSX_INLINE Animation::PlayType Animation::GetPlayType( void )const
{
	return m_playType;
}

PSX_INLINE UINT Animation::GetCurrFrame( void ) const
{
	return m_currFrame;
}

PSX_INLINE void Animation::SetPlayType( PlayType type )
{
	m_playType = type;
}

PSX_INLINE void Animation::SetSPF( FLOAT spf )
{
	m_spf = spf;
}

PSX_INLINE void Animation::SetCurrFrame( UINT frame )
{
	PSX_ThrowAssert( frame < m_frames.GetSize(), "Frame index out-of-bound." );
	m_currFrame = frame;
}

PSX_INLINE Pulse::BOOL Animation::TestPoint( INT x, INT y )
{
	return m_frames[m_currFrame]->m_pFrame->TestPoint( x, y );
}

#endif /* _ANIMATION_H_ */